#include "ObjMesh.h"
#include "ReportError.h"
#include "Graphics.h"

#include <D3DX9mesh.h>
#include <fstream>

ObjMesh::ObjMesh()
{
}
ObjMesh::~ObjMesh()
{
}
void ObjMesh::Load(const std::string& fileName)
{
	LoadGeometryFromOBJ(fileName);

	LoadMaterialsFromMTL(fileName);

	BuildMesh();

	   //// Load material textures
    //for ( int iMaterial = 0; iMaterial < m_Materials.size(); ++iMaterial )
    //{
    //    Material *pMaterial = m_Materials[iMaterial];

    //    if ( pMaterial->strTexture )
    //    {            
    //        pMaterial->pTextureRV10 = (ID3D10ShaderResourceView*) 1;

    //        if ( SUCCEEDED(DXUTFindDXSDKMediaFileCch( str, MAX_PATH, pMaterial->strTexture) ) )
    //        {
    //            DXUTGetGlobalResourceCache().CreateTextureFromFile( pd3dDevice, str, 
    //                &pMaterial->pTextureRV10, false ) ;
    //        }
    //    }
    //}

}

void ObjMesh::LoadGeometryFromOBJ(const std::string& fileName)
{    

	// Create temporary storage for the input data. Once the data has been loaded into
	// a reasonable format we can create a D3DXMesh object and load it with the mesh data.
	std::vector<Vec3> Positions;
	std::vector<Vec2> TexCoords;
	std::vector<Vec3> Normals;

	std::vector<DWORD> Indices;     
	std::vector<DWORD> Attributes;   
	std::vector<Material*> Materials;

	std::string MatName;

	// The first subset uses the default material
	Material* pMaterial = new Material();

	ZeroMemory( pMaterial, sizeof( Material ) );

	pMaterial->Ambient = D3DXVECTOR3( 0.2f, 0.2f, 0.2f );
	pMaterial->Diffuse = D3DXVECTOR3( 0.8f, 0.8f, 0.8f );
	pMaterial->Specular = D3DXVECTOR3( 1.0f, 1.0f, 1.0f );
	pMaterial->Shininess = 0;
	pMaterial->Alpha = 1.0f;
	pMaterial->HasSpecular = false;
	pMaterial->pTextureRV10 = NULL;

	Materials.push_back(pMaterial);

	bool done;

	std::ifstream fin;
	// Attempt to open the file.
	fin.open(fileName);

	if(fin.good())
	{
		// If the file exists and there are no problems then exit since we have the file name.
		done = false;
	}
	else
	{
		done = true;
		// If the file does not exist or there was an issue opening it then notify the user and repeat the process.
		fin.clear();
		ReportError("File " + fileName + " could not be opened.");
	}

	char input;
	// Read from the file and continue to read until the end of the file is reached.
	fin.get(input);
	while(!fin.eof())
	{
		// If the line starts with '#' it is a Comment
		if(input == '#')
		{
			// Comment
		}

		// If the line starts with 'v' then count either the vertex, the texture coordinates, or the normal vector.
		if(input == 'v')
		{
			fin.get(input);
			if(input == ' ') 
			{ 
				Vec3 vert;
				fin >> vert.x >> vert.y >> vert.z;

				Positions.push_back(vert);

				// Invert the Z vertex to change to left hand system.
			}

			if(input == 't') 
			{ 
				Vec2 tex;
				fin >> tex.x >> tex.y;

				TexCoords.push_back(tex);

				// Invert the V texture coordinates to left hand system.
			}

			if(input == 'n') 
			{ 
				Vec3 norm;
				fin >> norm.x >> norm.y >> norm.z;

				Normals.push_back(norm);

				// Invert the Z normal to change to left hand system.
			}
		}

		// If the line starts with 'f' then face
		if(input == 'f')
		{
			/*fin.get(input);
			if(input == ' ')
			{*/
			// Read the face data in backwards to convert it to a left hand system from right hand system.
			//VertexPNT

			/*fin >> faces[faceIndex].vIndex3 >> input >> faces[faceIndex].tIndex3 >> input >> faces[faceIndex].nIndex3
			>> faces[faceIndex].vIndex2 >> input >> faces[faceIndex].tIndex2 >> input >> faces[faceIndex].nIndex2
			>> faces[faceIndex].vIndex1 >> input >> faces[faceIndex].tIndex1 >> input >> faces[faceIndex].nIndex1;
			faceIndex++;*/


			// Face
			UINT iPosition, iTexCoord, iNormal;
			VertexPNT* vertex = new VertexPNT();

			//for each vert
			for( UINT iFace = 0; iFace < 3; iFace++ )
			{
				//ZeroMemory( &vertex, sizeof( VertexPNT ) );

				// OBJ format uses 1-based arrays
				fin >> iPosition;
				vertex->position = Positions[ iPosition - 1 ];

				if( '/' == fin.peek() )
				{
					fin.ignore();

					if( '/' != fin.peek() )
					{
						// Optional texture coordinate
						fin >> iTexCoord;
						vertex->tex0 = TexCoords[ iTexCoord - 1 ];
					}

					if( '/' == fin.peek() )
					{
						fin.ignore();

						// Optional vertex normal
						fin >> iNormal;
						vertex->normal = Normals[ iNormal - 1 ];
					}
				}

				// If a duplicate vertex doesn't exist, add this vertex to the Vertices
				// list. Store the index in the Indices array. The Vertices and Indices
				// lists will eventually become the Vertex Buffer and Index Buffer for
				// the mesh.
				
				// Add to the Vertices list
				m_Vertices.push_back(vertex);

				//save the verts that make a triangle face
				//obj file starts at index 1.
				m_Indices.push_back(iPosition - 1);

			}
			//m_Attributes.push_back( dwCurSubset );
		}

		//else if( input == 'mtllib' )
       // {
         //   // Material library
        //    fin >> MatName;
       // }

		//else if( input == 'usemtl' )
      //  {
       //     // Material

       //     WCHAR strName[MAX_PATH] = {0};
       //     fin >> strName;

  //          bool bFound = false;
  //          for( int iMaterial = 0; iMaterial < sizeof(m_Materials); iMaterial++ )
  //          {
  //              Material* pCurMaterial = m_Materials[iMaterial];

  //              if( 0 == wcscmp( pCurMaterial->strName, strName ) )
  //              {
  //                  bFound = true;
  //                  dwCurSubset = iMaterial;
  //                  break;
  //              }
  //          }

  //          if( !bFound )
  //          {
  //              pMaterial = new Material();
  //              if( pMaterial == NULL )
  //                  return E_OUTOFMEMORY;

  //              dwCurSubset = sizeof(m_Materials);

  //              InitMaterial( pMaterial );
  //              wcscpy_s( pMaterial->strName, MAX_PATH - 1, strName );

  //              m_Materials.push_back(pMaterial);
  //          }

		//}

		// Otherwise read in the remainder of the line.
		while(input != '\n')
		{
			fin.get(input);
		}

		// Start reading the beginning of the next line.
		fin.get(input);
	}

	// Close the file.
	fin.close();



}

void ObjMesh::LoadMaterialsFromMTL(const std::string& fileName)
{
}

void ObjMesh::BuildMesh()
{
	 // Create the encapsulated mesh
    ID3DX10Mesh *pMesh = NULL;

	UINT num = (sizeof(VertexPNTElements) / sizeof(VertexPNTElements[0]));

	float size1 =  m_Indices.size();
	float size2 =  m_Vertices.size();

	HRESULT result;
    result =  D3DX10CreateMesh( TheGraphics::Instance()->GetD3DDevice(),
                                VertexPNTElements,
                                num,
                                "POSITION",
                                size2,
                                size1,
                                D3DX10_MESH_32_BIT,
                                &pMesh );

	if(result == D3D_OK)
	{
	
		// Set the vertex data
		pMesh->SetVertexData( 0, (void*)&m_Vertices);
		//m_Vertices.RemoveAll();

		// Set the index data
		pMesh->SetIndexData( (void*)&m_Indices, m_Indices.size());
		// m_Indices.RemoveAll();

		// Set the attribute data
		pMesh->SetAttributeData((UINT*)&m_Attributes );
		//m_Attributes.RemoveAll();

		// Reorder the vertices according to subset and optimize the mesh for this graphics 
		// card's vertex cache. When rendering the mesh's triangle list the vertices will 
		// cache hit more often so it won't have to re-execute the vertex shader.
		pMesh->GenerateAdjacencyAndPointReps( 1e-6f );
		pMesh->Optimize( D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE, NULL, NULL );

		//pMesh->GetAttributeTable( NULL, &m_NumAttribTableEntries );
		//m_pAttribTable = new D3DX10_ATTRIBUTE_RANGE[m_NumAttribTableEntries];
		//pMesh->GetAttributeTable( m_pAttribTable, &m_NumAttribTableEntries );

		pMesh->CommitToDevice();

		m_pMesh = pMesh;
		}

}